#ifndef IMAGE_H
#define IMAGE_H


#include "config.h"

#include "imagepart.h"
#include <pixel32.h>
#include <image.h>
#include <QPainter>


#ifndef Q_LIKELY
#define Q_LIKELY
#endif

#ifndef Q_UNLIKELY
#define Q_UNLIKELY
#endif

#include "session.h"

#include <pixel32.h>
#include <QImage>

/**
 * @brief The Image class
 */
class Image : public QImage
{
public:

    Image(const Image &other);
    Image (QImage const &other = QImage (), const Session &s = Session ());
    Image (QSize const &size, const Session &s = Session ());
    Image (QString const &path);
    ~Image () {}

    void clear ();
    QString properties () const;
    QPixmap toPixmap () const;

    void setSession (const Session &session)  { m_session = session; }
    const Session &session () const { return m_session; }

    QImage toPreview () const;
    static int dpiToDpm (const int dpi);
    static int dpmToDpi (const int dpm);

private:
    Session m_session;
    int m_minNeighbourCount;
    int m_blackCount;

    QList<QImage> split () const;

    const Pixel32 *constBits () const { return (Pixel32 const *) QImage::constBits (); }
    Pixel32 *bits () { return (Pixel32*) QImage::bits (); }
    const Pixel32 *constScanLine (const int line) const { return (const Pixel32*) QImage::constScanLine (line); }
    Pixel32 *constScanLine (const int line) { return (Pixel32*) QImage::constScanLine (line); }

    // static
    static inline QRgb inverted32 (const QRgb color) { return color == Pixel32::BlackColor ? Pixel32::WhiteColor : Pixel32::BlackColor; }
    static inline QRgb inverted8 (const quint8 colorIndex) { return 1-colorIndex; }
    static Image fusionV (const QList<QImage> &list);
    static inline bool mustInvertOrphans () { const Config c; return c.optionInvertOrphanPixels (); }
    static inline bool mustInvertColors () { const Config c; return c.optionInvertColors (); }
    static inline int minPixNeighbours () { const Config c; return c.optionMinPixNeighbours (); }
    static void copyInto (const QImage &src, QImage *dest, int x, int y);

    // ------------ fonctions
    double scale () const { return m_session.scale (); }
    quint8 contrast () const { return m_session.contrast (); }

    QList<QRect> greenRects() const  { return m_session.greenRects (); }
    QList<QRect> redRects() const { return m_session.redRects (); }
    void toMono (const quint8 contrast);
    QRect rectOfInterest(Pixel32 const color, QRect const &fromRect) const;
    QRect minimumRectOfInterest() const;
    Image copyRect (QRect const &rectangle) const;
    void invertIfOrphan(uint *pixelPtr);
    void invertOrphanPixels(const int minNeighbourCount);
    QImage extractROI () const { return copyRect (minimumRectOfInterest ()); }
    int blackCount ();
    int whiteCount ();
    QImage extract(const QRect &rect, const double scale, const quint8 contrast) const;
};

//--------------------------------------------------------------------------------------------------
// optimisation du parcours des voisins

inline QPixmap Image::toPixmap() const {
            return QPixmap::fromImage (*this, Qt::AutoColor
                                       |Qt::ThresholdDither
                                       |Qt::AvoidDither
                                       |Qt::NoOpaqueDetection
                                       );
//    return QPixmap::fromImage (*this);
}

inline int Image::dpiToDpm(const int dpi) { return qRound ((qreal (dpi) / 2.54) * 100.0); }
inline int Image::dpmToDpi(const int dpm) { return qRound ((qreal (dpm) / 100.0) * 2.54); }

inline void Image::invertIfOrphan(uint *pixelPtr)
{
    const int w = width ();		// largeur de l'image en pixels

    const uint* neighbour = pixelPtr - w - 1; // haut gauche : 1 ligne de moins, 1 colonne de moins
    int neighbourCount = 0;

    const uint pixelColor = *pixelPtr;

    // début haut gauche
    if (*neighbour++ == pixelColor and Q_UNLIKELY (++neighbourCount >= m_minNeighbourCount)) return;
    // haut milieu
    if (*neighbour++ == pixelColor and Q_UNLIKELY (++neighbourCount >= m_minNeighbourCount)) return;
    // haut droit
    if (*neighbour == pixelColor and (++neighbourCount >= m_minNeighbourCount)) return;
    neighbour += w;
    // milieu droit
    if (*neighbour-- == pixelColor and ++neighbourCount >= m_minNeighbourCount) return;
    neighbour--; // skip
    // milieu gauche
    if (*neighbour == pixelColor and ++neighbourCount >= m_minNeighbourCount) return;
    neighbour += w;
    // bas gauche
    if (*neighbour++ == pixelColor and ++neighbourCount >= m_minNeighbourCount) return;
    // bas milieu
    if (*neighbour++ == pixelColor and ++neighbourCount >= m_minNeighbourCount) return;
    // bas droit
    if (*neighbour == pixelColor and ++neighbourCount >= m_minNeighbourCount) return;

    *pixelPtr = (pixelColor == Pixel32::WhiteColor) ? Pixel32::BlackColor : Pixel32::WhiteColor;
}

#endif // IMAGE_H
